package com.lq.memo.calendar;

import com.lq.memo.config.MemoConfig;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

/**
 * @program: memo
 * @description:
 * @author: LQ
 * @create: 2021-10-28 11:45
 */
@Component
@Data
public class LunarCalendar {

    @Autowired
    MemoConfig memoConfig;

    // 农历的年份
    private int year;
    private int month;
    private int day;
    // 农历的月份
    private String lunarMonth;
    // 闰的是哪个月
    public int leapMonth = 0;
    public Boolean leapNow = false;

    final static String[] CHINESE_NUMBER = {"一", "二", "三", "四", "五", "六", "七", "八", "九", "十", "十一", "十二"};
    static SimpleDateFormat chineseDateFormat = new SimpleDateFormat("yyyy年MM月dd日", Locale.CHINA);
    final static long[] LUNAR_INFO = new long[]{0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2, 0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977, 0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, 0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950, 0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, 0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5d0, 0x14573, 0x052d0, 0x0a9a8, 0x0e950, 0x06aa0, 0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, 0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b5a0, 0x195a6, 0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570, 0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x055c0, 0x0ab60, 0x096d5, 0x092e0, 0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5, 0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, 0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530, 0x05aa0, 0x076a3, 0x096d0, 0x04bd7, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, 0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0};

    /**
     * 农历部分假日
     */
    final static String[] lunarHoliday = new String[]{"0101 春节", "0115 元宵节", "0505 端午节", "0707 情人节", "0715 中元节", "0815 中秋节", "0909 重阳节", "1208 腊八节", "1224 小年", "0100 除夕"};

    /**
     * 公历部分节假日
     */
    final static String[] SOLAR_HOLIDAY = new String[]{"0101 元旦节", "0214 情人节", "0308 妇女节", "0312 植树节", "0315 消费者权益日", "0401 愚人节", "0501 劳动节", "0504 青年节", "0512 护士节", "0601 儿童节", "0701 建党节", "0801 建军节", "0808 父亲节", "0910 教师节", "1001 国庆节", "1225 圣诞"};

    /**
     * 传回农历 y年的总天数
     */
    final private static int yearDays(int y) {
        int i, sum = 348;
        for (i = 0x8000; i > 0x8; i >>= 1) {
            if ((LUNAR_INFO[y - 1900] & i) != 0) {
                sum += 1;
            }
        }
        return (sum + leapDays(y));
    }

    /**
     * 传回农历 y年闰月的天数
     */
    final private static int leapDays(int y) {
        if (leapMonth(y) != 0) {
            if ((LUNAR_INFO[y - 1900] & 0x10000) != 0) {
                return 30;
            } else {
                return 29;
            }
        } else {
            return 0;
        }
    }

    /**
     * 传回农历 y年闰哪个月 1-12 , 没闰传回 0
     */
    final private static int leapMonth(int y) {
        int result = (int) (LUNAR_INFO[y - 1900] & 0xf);
        return result;
    }

    /**
     * 传回农历 y年m月的总天数
     */
    final private static int monthDays(int y, int m) {
        if ((LUNAR_INFO[y - 1900] & (0x10000 >> m)) == 0) {
            return 29;
        } else {
            return 30;
        }
    }

    /**
     * 传回农历 y年的生肖
     *
     * @param year
     * @return
     */
    final public String animalsYear(int year) {
        final String[] Animals = new String[]{"鼠", "牛", "虎", "兔", "龙", "蛇", "马", "羊", "猴", "鸡", "狗", "猪"};
        return Animals[(year - 4) % 12];
    }

    /**
     * 传入 月日的offset 传回干支, 0=甲子
     *
     * @param num
     * @return
     */
    final private static String cyclicalm(int num) {
        final String[] Gan = new String[]{"甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸"};
        final String[] Zhi = new String[]{"子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥"};
        return (Gan[num % 10] + Zhi[num % 12]);
    }

    /**
     * 传入 offset 传回干支, 0=甲子
     *
     * @param year
     * @return
     */
    final public String cyclical(int year) {
        int num = year - 1900 + 36;
        return (cyclicalm(num));
    }

    public static String getChinaDayString(int day) {
        String chineseTen[] = {"初", "十", "廿", "卅"};
        int n = day % 10 == 0 ? 9 : day % 10 - 1;
        if (day > 30) {
            return "";
        }
        if (day == 10) {
            return "初十";
        } else {
            return chineseTen[day / 10] + CHINESE_NUMBER[n];
        }
    }

    /**
     * 传出y年m月d日对应的农历. yearCyl3:农历年与1864的相差数 ? monCyl4:从1900年1月31日以来,闰月数
     * dayCyl5:与1900年1月31日相差的天数,再加40 ?
     * <p>
     * isday: 这个参数为false---日期为节假日时，阴历日期就返回节假日 ，true---不管日期是否为节假日依然返回这天对应的阴历日期
     *
     * @return
     */
    public String getLunarDate(int year_log, int month_log, int day_log, boolean isday) {
        int yearCyl, monCyl, dayCyl;
        setLeapNow(false);
        setLeapMonth(0);
        String nowadays;
        Date baseDate = null;
        Date nowaday = null;
        try {
            baseDate = chineseDateFormat.parse("1900年1月31日");
        } catch (ParseException e) {
            e.printStackTrace();
        }

        nowadays = year_log + "年" + month_log + "月" + day_log + "日";
        try {
            nowaday = chineseDateFormat.parse(nowadays);
        } catch (ParseException e) {
            e.printStackTrace(); // To change body of catch statement use
            // Options | File Templates.
        }

        // 求出和1900年1月31日相差的天数
        int offset = (int) ((nowaday.getTime() - baseDate.getTime()) / 86400000L);
        dayCyl = offset + 40;
        monCyl = 14;

        // 用offset减去每农历年的天数
        // 计算当天是农历第几天
        // i最终结果是农历的年份
        // offset是当年的第几天
        int iYear, daysOfYear = 0;
        for (iYear = 1900; iYear < 10000 && offset > 0; iYear++) {
            daysOfYear = yearDays(iYear);
            offset -= daysOfYear;
            monCyl += 12;
        }
        if (offset < 0) {
            offset += daysOfYear;
            iYear--;
            monCyl -= 12;
        }
        // 农历年份
        year = iYear;
        // 设置公历对应的农历年份
        setYear(year);

        yearCyl = iYear - 1864;
        // 闰哪个月,1-12
        leapMonth = leapMonth(iYear);
        boolean leap = false;

        // 用当年的天数offset,逐个减去每月（农历）的天数，求出当天是本月的第几天
        int iMonth, daysOfMonth = 0;
        for (iMonth = 1; iMonth < 13 && offset > 0; iMonth++) {
            // 闰月
            if (leapMonth > 0 && iMonth == (leapMonth + 1) && !leap) {
                --iMonth;
                leap = true;
                setLeapNow(true);
                daysOfMonth = leapDays(year);
            } else {
                daysOfMonth = monthDays(year, iMonth);
            }
            offset -= daysOfMonth;
            // 解除闰月
            if (leap && iMonth == (leapMonth + 1)) {
                leap = false;
            }
            if (!leap) {
                monCyl++;
            }
        }
        // offset为0时，并且刚才计算的月份是闰月，要校正
        if (offset == 0 && leapMonth > 0 && iMonth == leapMonth + 1) {
            if (leap) {
                leap = false;
                setLeapNow(false);
            } else {
                leap = true;
                setLeapNow(true);
                --iMonth;
                --monCyl;
            }
        }
        // offset小于0时，也要校正
        if (offset < 0) {
            offset += daysOfMonth;
            --iMonth;
            --monCyl;
        }
        setMonth(iMonth);
        // 设置对应的阴历月份
        setLunarMonth(CHINESE_NUMBER[month - 1] + "月");
        day = offset + 1;

        if (!isday) {
            // 如果日期为节假日则阴历日期则返回节假日
            // setLeapMonth(leapMonth);
            for (int i = 0; i < SOLAR_HOLIDAY.length; i++) {
                // 返回公历节假日名称
                String sd = SOLAR_HOLIDAY[i].split(" ")[0]; // 节假日的日期
                String sdv = SOLAR_HOLIDAY[i].split(" ")[1]; // 节假日的名称
                String smonth_v = month_log + "";
                String sday_v = day_log + "";
                String smd = "";
                if (month_log < 10) {
                    smonth_v = "0" + month_log;
                }
                if (day_log < 10) {
                    sday_v = "0" + day_log;
                }
                smd = smonth_v + sday_v;
                if (sd.trim().equals(smd.trim())) {
                    return sdv;
                }
            }

            for (int i = 0; i < lunarHoliday.length; i++) {
                // 返回农历节假日名称
                String ld = lunarHoliday[i].split(" ")[0]; // 节假日的日期
                String ldv = lunarHoliday[i].split(" ")[1]; // 节假日的名称
                String lmonth_v = month + "";
                String lday_v = day + "";
                String lmd = "";
                if (month < 10) {
                    lmonth_v = "0" + month;
                }
                if (day < 10) {
                    lday_v = "0" + day;
                }
                lmd = lmonth_v + lday_v;
                // 除夕夜需要特殊处理
                if ("12".equals(lmonth_v)) {
                    if ((daysOfMonth == 29 && day == 29) || (daysOfMonth == 30 && day == 30)) {
                        return ldv;
                    }
                }
                if (ld.trim().equals(lmd.trim())) {
                    return ldv;
                }
            }
        }
        if (day == 1) {
            if (getLeapNow() && (getMonth() == getLeapMonth())) {
                return memoConfig.getLeapMonthIdentifier() + CHINESE_NUMBER[month - 1] + "月";
            } else {
                return CHINESE_NUMBER[month - 1] + "月";
            }
        } else {
            return getChinaDayString(day);
        }
    }

    /**
     * 获取农历,返回月日
     * 格式MM-dd
     * 闰月加上闰字,如:闰04-23(默认闰,可以在yml配置)
     *
     * @param year_log
     * @param month_log
     * @param day_log
     * @return
     */
    public String getLunarCalenderMonthDay(int year_log, int month_log, int day_log) {
        getLunarDate(year_log, month_log, day_log, true);
        String myMonth = getMonth() + "";
        if (getMonth() < 10) {
            myMonth = "0" + myMonth;
        }
        if (getLeapNow() && (getMonth() == getLeapMonth())) {
            myMonth = memoConfig.getLeapMonthIdentifier() + myMonth;
        }
        String myDay = getDay() + "";
        if (getDay() < 10) {
            myDay = "0" + myDay;
        }
        return myMonth + "-" + myDay;
    }

    /**
     * 获取农历,返回月日
     * 格式yyyy-MM-dd
     * 闰月加上闰字,如:2020-闰04-23
     *
     * @param year_log  新历年
     * @param month_log 新历月
     * @param day_log   新历日
     * @return 年月日
     */
    public String getLunarCalender(int year_log, int month_log, int day_log) {
        String lunarCalenderMonthDay = getLunarCalenderMonthDay(year_log, month_log, day_log);
        return getYear() + "-" + lunarCalenderMonthDay;
    }

    @Override
    public String toString() {
        if (CHINESE_NUMBER[month - 1] == "一" && getChinaDayString(day) == "初一") {
            return "农历" + year + "年";
        } else if (getChinaDayString(day) == "初一") {
            return CHINESE_NUMBER[month - 1] + "月";
        } else {
            return getChinaDayString(day);
        }
    }

    public static void main(String[] args) {
        System.out.println(new LunarCalendar().getLunarDate(2021, 12, 4, false));
        System.out.println(new LunarCalendar().getLunarCalender(2021, 5, 23));
        System.out.println(new LunarCalendar().getLunarCalenderMonthDay(2021, 5, 23));
    }
}
